home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_400
/
422_02
/
misc
/
basic.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-03-20
|
21KB
|
916 lines
/*
* MICRO BASIC:
*
* This is a very simple INTEGER BASIC interpreter that I wrote a number of
* years ago, and subsequently ported to MICRO-C. While not the greatest
* example of coding style (it was a quick and dirty hack job), It is quite
* instructive, as a simple but fairly complete interpreter.
*
* Variables:
* 260 Numeric variables: A0-A9 ... Z0-Z9
* 260 Character variables: A0$-A9$ ... Z0$-Z9$
*
* NOTE: For convenience the '0' variables can be referenced by letter
* only. IE: A is equivalent to A0 ... Z$ is equivalent to Z0$
*
* Statements:
* LET (default) - variable = expression
* EXIT - Terminate MICRO-BASIC
* LIST [start,[end]] - List program lines
* LIST#n ... - List program to file (0-9)
* NEW - Erase program and variables
* RUN [line] - Run program
* CLEAR - Erase variables only
* GOSUB line - Call a subroutine
* GOTO line - Jump to line
* RETURN - Return from subroutine
* PRINT expr[,expr ...] - Print to console
* PRINT#n ... - Print to file (0-9)
* FOR v=init TO limit [STEP increment] - Perform a counted loop
* NEXT [v] - End counted loop
* IF test THEN line - Conditional goto
* IF test THEN statement - Conditional statement (next statement only)
* LIF test THEN statements- LONG IF (all statements to end of line)
* REM - Comment... reminder of line is ignored
* STOP - Terminate program & issue message
* END - Terminate program with no message
* INPUT var - Get value for variable
* INPUT "prompt",var - Get value of variable with prompt
* NOTE: prompt must be a constant string, however you can use
* a char variable in prompt by concatinating it to such
* a string: INPUT ""+a$,b$
* INPUT#n,var - Get value for variable from file (0-9)
* OPEN#n,"name","opts" - Open file (0-9), opts are same as "fopen()"
* CLOSE#n - Close file (0-9)
*
* Operators:
* + - Addition, string concatination
* - - Unary minus, subtraction
* *, /, %, - multiplication, division, modulus
* &, |, ^ - AND, OR, Exclusive OR
* =, <> - Assignment/test equal, test NOTequal (num or string)
* <, <=, >, >= - LT, LE, GT, GE (numbers only)
* ! - Unary NOT
*
* Functions:
* CHR$(value) - Returns character of passed value
* STR$(value) - Returns ASCII string of value's digits
* ASC(char) - Returns value of passed character
* ABS(value) - Returns absolute value of argument
*
* If want to use a different compiler, take note:
*
* - Make sure 'fgets' does not include the trailing NEWLINE
* You can use "microc.h" from utilities source directory.
*
* - Make sure that 'isalpha' and 'isdigit' can deal with negative
* character values (most macro implementations can't). If not,
* include those functions from the MICRO-C library.
*
* - Modify the declaration of 'savjmp' to whatever is appropriate
* for your compilers 'setjmp' and 'longjmp' functions.
*
* Copyright 1982-1994 Dave Dunfield
* All rights reserved.
*
* Permission granted for personal (non-commercial) use only.
*
* Compile command: cc basic -fop
*/
#include <stdio.h>
/* Fixed parameters */
#define BUFFER_SIZE 100 /* input buffer size */
#define NUM_VAR 260 /* number of variables */
#define SA_SIZE 100 /* string accumulator size */
/* Reserved word grouping */
#define SEC 21 /* secondary keywords */
#define OPS SEC+3 /* first operator */
#define SOPS OPS+14 /* first string function */
#define NOPS SOPS+2 /* first numeric function */
/* Control stack constant identifiers */
#define FOR 1000 /* indicate FOR statement */
#define GOSUB FOR+1 /* indicate GOSUB statement */
struct line_record {
unsigned Lnumber;
struct line_record *Llink;
char Ltext[]; };
static char *reserved_words[] = {
"LET", "EXIT", "LIST", "NEW", "RUN", "CLEAR", "GOSUB", "GOTO",
"RETURN", "PRINT", "FOR", "NEXT", "IF", "LIF", "REM", "STOP",
"END", "INPUT", "OPEN", "CLOSE", "TO", "STEP", "THEN",
"+", "-", "*", "/", "%", "&", "|", "^",
"=", "<>", "<=", "<", ">=", ">",
"CHR$(", "STR$(", "ASC(", "ABS(",
0 };
static char priority[] = {
0, 1, 1, 2, 2, 2, 3, 3, 3,
1, 1, 1, 1, 1, 1
};
static char *error_messages[] = {
"Syntax",
"Illegal program",
"Illegal direct",
"Line number",
"Wrong type",
"Divide by zero",
"Nesting",
"File not open",
"File already open",
"Input"
};
char sa1[SA_SIZE], sa2[SA_SIZE]; /* string accumulators */
struct line_record *pgm_start, *runptr; /* Line tracking pointers */
int num_vars[NUM_VAR] = { 0 }; /* Numeric variables */
char *char_vars[NUM_VAR] = { 0 }; /* Character variables */
FILE *files[10]={0}, *filein, *fileout; /* File unit numbers */
int savjmp[3]; /* Save area for set/longjmp */
/* Misc. global variables */
char *cmdptr, buffer[BUFFER_SIZE], mode, expr_type, nest;
unsigned line, ctl_ptr = 0, ctl_stk[100];
/* test for end of expression */
isend(c)
char c;
{
if((c >= (0xff80+SEC)) && (c < (0xff80+OPS)))
return(1);
return (c == '\0') || (c == ':') || (c == ')') || (c == ',');
}
/* test for end of statement */
islend(c)
char c;
{
return (c == '\0') || (c == ':');
}
/* test for terminator character */
isterm(c)
char c;
{
return (c == ' ') || (c == '\t');
}
/* advance to next non-blank */
char skip_blank()
{
while(isterm(*cmdptr))
++cmdptr;
return *cmdptr;
}
/* advance to., return and skip next non blank */
char skip_next()
{
char c;
while(isterm(c=*cmdptr))
++cmdptr;
if(c)
++cmdptr;
return c;
}
/* translate to special codes */
translate()
{
unsigned value;
char *ptr, c;
cmdptr = ptr = buffer;
while(c = *cmdptr) {
if(value = lookup(reserved_words))
*ptr++ = value + 0x80;
else {
*ptr++ = c;
++cmdptr;
if(c == '"') { /* double quote */
while((c = *cmdptr) && (c != '"')) {
++cmdptr;
*ptr++ = c; }
*ptr++ = *cmdptr++; } } }
*ptr = 0;
cmdptr = buffer;
}
/* prompt for and get a line from standard input */
char get_line(prompt)
char *prompt;
{
fputs(prompt, stdout);
fgets(buffer, BUFFER_SIZE, stdin);
translate();
return skip_blank();
}
/* get a number from the input buffer */
get_num()
{
unsigned value;
char c;
value = 0;
while(isdigit(c=*cmdptr)) {
++cmdptr;
value = (value * 10) + (c - '0'); }
return value;
}
/* lookup up word from command line in table */
lookup(table)
char *table[];
{
unsigned i;
char *cptr, *optr;
optr = cmdptr;
for(i=0; cptr = table[i]; ++i) {
while((*cptr) && (*cptr == toupper(*cmdptr))) {
++cptr;
++cmdptr; }
if(!*cptr) {
skip_blank();
return i+1; }
cmdptr = optr; }
return 0;
}
/* main program */
main()
{
unsigned value;
char cmd;
pgm_start = 0;
setjmp(savjmp);
for(;;) { /* main command loop */
mode = ctl_ptr = 0;
while(!get_line("Ready\n")); /* insure we get command */
if(0 > (cmd = skip_blank())) {
++cmdptr;
execute(cmd); }
else { /* not a known command */
if(isdigit(*cmdptr)) { /* editing... */
value = get_num(); /* get line number */
delete_line(value); /* delete the old */
if(skip_blank())
insert_line(value); } /* insert the new */
else
execute(1); } } /* assume let */
}
/* delete a line from the program */
delete_line(lino)
unsigned lino;
{
struct line_record *cptr, *bptr;
if(!(cptr = pgm_start)) /* no lines in pgm */
return;
do {
if(lino == cptr->Lnumber) { /* we have line to delete */
if(cptr == pgm_start) { /* first line in pgm */
pgm_start = cptr->Llink;
return; }
else {
bptr->Llink = cptr->Llink; /* skip it in linked list */
free(cptr); } } /* let it go */
bptr = cptr; }
while(cptr = cptr->Llink);
}
/* Insert a line into the program */
insert_line(lino)
unsigned lino;
{
unsigned i;
struct line_record *cptr, *bptr, *optr;
char *ptr;
ptr = cmdptr;
for(i=5; *ptr; ++i)
++ptr